package app.framework.security.authentication;

import app.framework.constant.AuthConstants;
import app.framework.security.csrf.CsrfTokenUtil;
import app.framework.security.util.CustomHttpServletResponseWrapper;
import jasmine.framework.common.util.CheckUtil;
import jasmine.framework.common.util.ObjectUtil;
import jasmine.framework.common.util.StringUtil;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.RememberMeServices;
import org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.List;

/**
 * @author mh.z
 */
public class CsrfSupportRememberMeServices implements RememberMeServices {
    private RememberMeServices targetRememberMeServices;
    private String rememberMeParameterName;
    private String rememberMeCookieName;

    private static final String DEFAULT_REMEMBER_ME_PARAMETER_NAME = AuthConstants.REMEMBER_ME_REQUEST_PARAM_NAME;
    private static final String DEFAULT_REMEMBER_ME_COOKIE_NAME = AuthConstants.REMEMBER_ME_COOKIE_NAME;

    public CsrfSupportRememberMeServices(String key, UserDetailsService userDetailsService) {
        this.targetRememberMeServices = new TokenBasedRememberMeServices(key, userDetailsService);
        this.rememberMeParameterName = DEFAULT_REMEMBER_ME_PARAMETER_NAME;
        this.rememberMeCookieName = DEFAULT_REMEMBER_ME_COOKIE_NAME;
    }

    public String getRememberMeParameterName() {
        return rememberMeParameterName;
    }

    public void setRememberMeParameterName(String rememberMeParameterName) {
        this.rememberMeParameterName = rememberMeParameterName;
    }

    public String getRememberMeCookieName() {
        return rememberMeCookieName;
    }

    public void setRememberMeCookieName(String rememberMeCookieName) {
        this.rememberMeCookieName = rememberMeCookieName;
    }

    @Override
    public Authentication autoLogin(HttpServletRequest request, HttpServletResponse response) {
        Authentication authentication = targetRememberMeServices.autoLogin(request, response);

        if (authentication != null) {
            String rememberMeToken = CsrfTokenUtil.obtainRememberMeToken(request);
            // 添加“记住我"令牌到会话中
            HttpSession httpSession = request.getSession(true);
            httpSession.setAttribute(AuthConstants.REMEMBER_ME_SESSION_ATTR_NAME, rememberMeToken);

            // 设置 CSRF 令牌
            CsrfTokenUtil.setCsrfToken(request, response);
        }

        return authentication;
    }

    @Override
    public void loginFail(HttpServletRequest request, HttpServletResponse response) {
        targetRememberMeServices.loginFail(request, response);
    }

    @Override
    public void loginSuccess(HttpServletRequest request, HttpServletResponse response,
                             Authentication successfulAuthentication) {
        CustomHttpServletResponseWrapper customResponse = new CustomHttpServletResponseWrapper(response);
        targetRememberMeServices.loginSuccess(request, customResponse, successfulAuthentication);

        Boolean rememberMe = ObjectUtil.parseBoolean(request.getParameter(rememberMeParameterName));
        // 若登录时未勾上"记住我"，则清除"记住我"的cookie
        if (!Boolean.TRUE.equals(rememberMe)) {
            Cookie cookie = new Cookie(rememberMeCookieName, null);
            cookie.setMaxAge(0);
            String contextPath = request.getContextPath();
            String cookiePath = (contextPath.length() > 0)
                    ? contextPath : "/";
            cookie.setPath(cookiePath);

            response.addCookie(cookie);
        } else {
            List<Cookie> cookieList = customResponse.getCookies();
            String rememberMeToken = obtainCookieValue(cookieList, AuthConstants.REMEMBER_ME_COOKIE_NAME);
            // 添加“记住我"令牌到会话中
            HttpSession httpSession = request.getSession(true);
            httpSession.setAttribute(AuthConstants.REMEMBER_ME_SESSION_ATTR_NAME, rememberMeToken);
        }
    }

    /**
     * 查找指定 cookie 的值
     *
     * @param cookies
     * @param cookieName
     * @return
     */
    protected String obtainCookieValue(List<Cookie> cookies, String cookieName) {
        CheckUtil.notNull(cookies, "cookies null");
        CheckUtil.notNull(cookieName, "cookieName null");

        for (Cookie cookie : cookies) {
            if (StringUtil.equals(cookie.getName(), cookieName)) {
                return cookie.getValue();
            }
        }

        return null;
    }

}
